﻿using jvm.rtda;
using jvm.rtda.frame;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace jvm.mm
{
    class MemoryManager
    {
        static GcSpace youngSpace;
        static GcSpace survival1;
        static GcSpace survival2;
        static GcSpace oldSpace;
        static int nextSurvival = 1;
        static Jvm jvm;
        static long gcNumber;

        public static void Init(Jvm jvm0)
        {
            jvm = jvm0;
            youngSpace = new GcSpace(2 * 1024);
            survival1 = new GcSpace(1 * 1024);
            survival2 = new GcSpace(1 * 1024);
            oldSpace = new GcSpace(8 * 1024 * 1024);
        }

        public static JObject Alloc(JObject obj)
        {
            int size = Marshal.SizeOf(obj);
            if (youngSpace.IsFull(size))
            {
                GC();
            }
            return youngSpace.Alloc(obj);
        }

        static string SizeString(int size)
        {
            if (size < 1024)
            {
                return size + "字节";
            }
            else if (size < 1024 * 1024)
            {
                float f = size * 1.00f / 1024;
                return f.ToString("#0.00") + "k";
            }
            else
            {
                float f = size * 1.00f / 1024 / 1024;
                return f.ToString("#0.00") + "M";
            }
        }

        static void GC()
        {
            // 暂停线程
            foreach (JThread thread in jvm.threads)
            {
                if (thread.sysThread != Thread.CurrentThread)
                {
                    thread.sysThread.Suspend();
                }
            }
            // 标记引用
            List<JObject> objects = Sign();
            // 拷贝
            GcSpace validSurvivals;
            if (nextSurvival == 1)
            {
                validSurvivals = survival1;
                nextSurvival = 2;
                survival2.Clear();
            }
            else
            {
                validSurvivals = survival2;
                nextSurvival = 1;
                survival1.Clear();
            }

            Tuple<bool, int> tuple = CopyTo(objects, validSurvivals, false, false);
            if (!tuple.Item1)
            {
                if (oldSpace.IsFull(tuple.Item2))
                {
                    oldSpace.Clear();
                    tuple = CopyTo(objects, oldSpace, true, true);
                    if (!tuple.Item1)
                    {
                        Console.WriteLine("java.lang.OutOfMemoryError");
                        Environment.Exit(0);
                    }
                }
                else
                {
                    CopyTo(objects, oldSpace, true, false);
                }
            }
            int recycle = youngSpace.curSize - tuple.Item2;
            youngSpace.Clear();
            gcNumber++;
            // 恢复线程
            foreach (JThread thread in jvm.threads)
            {
                if (thread.sysThread != Thread.CurrentThread)
                {
                    thread.sysThread.Resume();
                }
            }
            Console.WriteLine("本次GC回收：{0}", SizeString(recycle));
        }

        static List<JObject> Sign()
        {
            List<JObject> objects = new List<JObject>();
            foreach (JThread thread in jvm.threads)
            {
                foreach (JFrame frame in thread.GetFrames())
                {
                    foreach (Slot slot in frame.localVars.slots)
                    {
                        if (slot.reference != null)
                        {
                            if (slot.reference.gcNumber != gcNumber)
                            {
                                slot.reference.weakreference.Clear();
                                slot.reference.gcNumber = gcNumber;
                                objects.Add(slot.reference);
                            }
                            slot.reference.weakreference.Add(slot);
                        }
                    }

                    for (int i = 0; i < frame.operandStack.size; i++)
                    {
                        Slot slot = frame.operandStack.slots[i];
                        if (slot.reference != null)
                        {
                            if (slot.reference.gcNumber != gcNumber)
                            {
                                slot.reference.weakreference.Clear();
                                slot.reference.gcNumber = gcNumber;
                            }
                            slot.reference.weakreference.Add(slot);
                        }
                    }
                }
            }
            return objects;
        }

        static Tuple<bool, int> CopyTo(List<JObject> objects, GcSpace validSurvivals, bool inOld, bool moveAll)
        {
            bool full = false;
            int allSize = 0;
            foreach (JObject obj in objects)
            {
                if (!moveAll && obj.inOld)
                {
                    // minor gc不回收old区的对象
                    continue;
                }
                if (obj.gcNumber != gcNumber)
                {
                    // 已经复制过了
                    continue;
                }
                int size = Marshal.SizeOf(obj);
                allSize += size;
                if (full)
                {
                    continue;
                }

                if (validSurvivals.IsFull(size))
                {
                    full = true;
                    continue;
                }
                JObject newObj = validSurvivals.Alloc(obj);
                newObj.gcNumber++;
                newObj.inOld = inOld;
                foreach (Slot weakSlot in obj.weakreference.slots)
                {
                    if (weakSlot != null)
                    {
                        weakSlot.reference = newObj;
                    }
                }
            }
            return new Tuple<bool, int>(!full, allSize);
        }
    }
}
